﻿using System.Collections.Generic;
using CoreEx.Common.Extensions;
using CoreEx.Common.Proxy;
using CoreEx.DynamicInvoker;
using LinFu.AOP.Interfaces;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;


namespace CoreEx.Database.Client.Implementation
{
    /// <summary>
    /// Base class for implementing database interceptors.
    /// </summary>
    /// <remarks>
    /// This class also takes care of invoking any <see cref="IProfiler{T}"/> instances.
    /// </remarks>
    /// <typeparam name="T">The type of </typeparam>
    public abstract class InvokeWrapperBase<T> : IInvokeWrapper<T>,IInitialize 
    {
        /// <summary>
        /// A list of profilers that will be invoked
        /// </summary>
        private IEnumerable<IProfiler<T>>  _profilers;
        
        /// <summary>
        /// Gets the <see cref="IServiceContainer"/> instance.
        /// </summary>
        protected IServiceContainer ServiceContainer { get; private set; }
        

        /// <summary>
        /// Gets the actual target for method invocation.
        /// </summary>
        public T ActualTarget { get; private set; }


        /// <summary>
        /// Creates a new instance of the <see cref="InvokeWrapperBase{T}"/>.
        /// </summary>
        /// <param name="actualTarget">The actual target for method invocation.</param>
        protected InvokeWrapperBase(T actualTarget)
        {
            ActualTarget = actualTarget;
        }

        /// <summary>
        /// Initializes the <see cref="InvokeWrapperBase{T}"/> instance.
        /// </summary>
        /// <param name="source">The <see cref="IServiceContainer"/> that created this instance.</param>
        public virtual void Initialize(IServiceContainer source)
        {
            ServiceContainer = source;
            _profilers = source.GetServices <IProfiler<T>>();
        }

        /// <summary>
        /// Fires before the actual target method is invoked.
        /// </summary>
        /// <param name="info">Contains information about the method call.</param>
        public virtual void BeforeInvoke(IInvocationInfo info)
        {
            _profilers.ForEach(p => p.BeforeInvoke(info));
        }

        /// <summary>
        /// Fires after the actual target method has been invoked.
        /// </summary>
        /// <param name="info">Contains information about the method call.</param>
        /// <param name="returnValue">The value returned from the method call.</param>
        public virtual void AfterInvoke(IInvocationInfo info, object returnValue)
        {
            _profilers.ForEach(p => p.AfterInvoke(info,returnValue));
        }

        /// <summary>
        /// Invokes the target method on the <see cref="ActualTarget"/>.
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public virtual object DoInvoke(IInvocationInfo info)
        {
            if (info.TargetMethod.DeclaringType == typeof(IActualTarget))
            {
                return ActualTarget;
            }
            return info.TargetMethod.DynamicInvoke(ActualTarget, info.Arguments);            
        }
        
    }
}
